﻿#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
/*
 * 下面是用 C++（OpenCV 实现）模拟 “全能扫描王” 文章中提到的核心扫描功能（包括图像矫正与二值化）的代码。步骤如下：
灰度 + 高斯模糊 + 膨胀 + Canny 边缘检测（轮廓易闭合）
轮廓提取，筛选最大四边形轮廓=
透视变换校正扫描区域
可选二值化处理

 效果非常的差，等待后续的调整优化
 * */

// 计算轮廓面积最大近似四边形
bool findDocumentContour(const Mat& edged, vector<Point>& quad) {
    vector<vector<Point>> contours;
    findContours(edged, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
    double maxArea = 0;
    vector<Point> best;
    for (auto &c : contours) {
        double peri = arcLength(c, true);
        vector<Point> approx;
        approxPolyDP(c, approx, 0.02 * peri, true);
        if (approx.size() == 4) {
            double area = contourArea(approx);
            if (area > maxArea) {
                maxArea = area;
                best = approx;
            }
        }
    }
    if (best.size() == 4) {
        quad = best;
        return true;
    }
    return false;
}

// 对四边形排序: TL, TR, BR, BL
vector<Point2f> sortQuad(const vector<Point>& quad) {
    vector<Point2f> pts;
    for (auto&p : quad) pts.emplace_back(p);
    sort(pts.begin(), pts.end(), [](Point2f a, Point2f b) {
        return a.y < b.y || (fabs(a.y - b.y) < 1e-3 && a.x < b.x);
    });
    vector<Point2f> res(4);
    res[0] = pts[0]; res[1] = pts[1];
    res[3] = pts[2]; res[2] = pts[3];
    return res;
}

int main() {
    // ✅ 本地图片路径，替换为你自己的图片路径
    string imagePath = "H:\\DWORPLACETEST\\projecttest\\Document-Scanner-main\\images\\dollar_bill.jpeg";

    Mat src = imread(imagePath);
    if (src.empty()) {
        cerr << "Can't open image: " << imagePath << endl;
        return -1;
    }

    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    GaussianBlur(gray, gray, Size(5, 5), 0);

    Mat bin;
    adaptiveThreshold(gray, bin, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 15);

    Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat dilated;
    dilate(bin, dilated, kernel);

    Mat edged;
    Canny(dilated, edged, 75, 200);

    vector<Point> quad;
    if (!findDocumentContour(edged, quad)) {
        cerr << "Document contour not found\n";
        return -1;
    }

    auto srcPts = sortQuad(quad);
    double w1 = norm(srcPts[0] - srcPts[1]);
    double w2 = norm(srcPts[2] - srcPts[3]);
    double h1 = norm(srcPts[0] - srcPts[3]);
    double h2 = norm(srcPts[1] - srcPts[2]);
    Size dstSize((int)max(w1, w2), (int)max(h1, h2));

    vector<Point2f> dstPts = {
            Point2f(0, 0),
            Point2f(dstSize.width - 1, 0),
            Point2f(dstSize.width - 1, dstSize.height - 1),
            Point2f(0, dstSize.height - 1)
    };
    Mat M = getPerspectiveTransform(srcPts, dstPts);
    Mat scanned;
    warpPerspective(src, scanned, M, dstSize);

    Mat scannedGray, scannedBin;
    cvtColor(scanned, scannedGray, COLOR_BGR2GRAY);
    adaptiveThreshold(scannedGray, scannedBin, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 15);

    imshow("Original", src);
    imshow("Scanned", scannedBin);
    waitKey(0);
    return 0;
}
